#ifndef AI_H
#define AI_H



#include "includes.h"
#include "init.h"
#include "rules.h"
#include "units.h"
#include "map.h"
#include "players.h"



class player;
class object;



/** @brief Class that stores all information about game state e.g. players and their units, map, map of units, current player and some help computes
*	It is used for AI algorithm purposes and score computing.
*
*   \image html ai.jpeg
*/
class game_state
{

public:

	/**	@brief			Constructor, that makes complete clone of given parameters expect help computes
	*	@param pl_c		number of players, constraint: pl_c >= 2
	*	@param c_pl_id	current player's id, constraint: 0 <= c_pl_id < pl_c
	*	@param pl		array of players, constraint: pl != 0
	*	@param obj		2D array of objects that are currently in game, constraint: obj != 0
	*	@param mp		game map, constraint: mp != 0
	*	@param sc		current player's score
	*	@param ep		estimeted defensive places - one square contains number of places from which you can attack there
	*	@param rp		reachable places - false if unreachable, true if reachable
	*	@param dp		dangerous places - one square contains total maximum, that can enemy's units cause there by one shot
	*	@param dpn		dangerous places nobody - one square contains total maximum, that can enemy's units cause there by one shot, but it doesnt consider other units as shield
	*	@param ap		attackable places - one square contains total maximum, that can current player's units cause there by one shot
	*	@param rgp		range places - vector of maps, where one square contains number of enemy's, that can be attacked from there by unit ( row ~ unit )
	*/
	game_state( unsigned int pl_c, unsigned int c_pl_id, player** pl, array2d< object* >* obj, array2d< unsigned int >* mp, double sc = 0, array2d< unsigned int >* ep = 0, array2d< bool >* rp = 0, array2d< unsigned int >* dp = 0, array2d< unsigned int >* dpn = 0, array2d< unsigned int >* ap = 0, vector< array2d< unsigned int >* >* rgp = 0 );

	//! @brief Destructor, that deletes all information including help computes
	~game_state();

	/** @brief	Checks if this game state is the same with given
	*	@param	s game state to check with
	*	@return true if the game states are the same, else false
	*/
	bool equals( game_state* s );
	/**	@brief	makes deep clone of game state including help computes except map
	*	@return	pointer to the clone
	*/
	game_state* clone() const;
	/** @brief	gets hash number of the state
	*	@return	hash number
	*/
	unsigned long long int get_hash();

	//! @brief Updates all help structures
	void update_all_structures();
	//! @brief Updates help structures except those, that are dependent only on terrain
	void update_structures();
	//! @brief Updates help structures that are used for score calculation
	void update_score_structures();

	//! @brief Deletes all help structures
	void clear_all_structures();
	//! @brief Deletes help structures except those, that are dependent only on terrain
	void clear_structures();

	/** @brief Gets structures, that are dependent only on terrain, from another state and sets them to this
	*	@param state state, where to get structures from, constraint: state != 0
	*/
	void get_map_structures_from_state( const game_state* state );

	/** @brief	Computes another player's score
	*	@return player's score to compute score for
	*/
	double another_plrs_score( unsigned int aplr );


	//! game map
	array2d< unsigned int >* map;

	//! array of players
	player** plrs;

	//! number of players
	unsigned int players_count;

	//! current player's id
	unsigned int current_player_id;

	//! 2D array of objects that are currently in game
	array2d< object* >* objects;
	

	//! one square contains number of places from which you can attack there
	array2d< unsigned int >* estimated_places;

	//! one square contains total maximum, that can enemy's units cause there by one shot
	array2d< unsigned int >* dangerous_places;

	//! one square contains total maximum, that can enemy's units cause there by one shot, but it doesnt consider other units as shield
	array2d< unsigned int >* dangerous_places_nobody;

	//! one square contains total maximum, that can current player's units cause there by one shot
	array2d< unsigned int >* attackable_places;

	//! one square contains number of enemy's units, that can be attacked from there
	vector< array2d< unsigned int >* >* range_places;

	//! false if place is unreachable, true if it is reachable
	array2d< bool >* reachable_places;

	//! current player's score
	double score;

};



//! @brief Name space with functions for help computes especially for artificial intelligence
namespace ai_support_computations
{
	/** @brief	Estimates general dangerousness of places
	*	@param	state state for which the places will be computed
	*	@return	2D array where one square contains number of places from which you can attack there
	*/
	array2d< unsigned int >* map_places_estimate( game_state* state );

	/**	@brief	Computes which places are reachable
	*	@param	state state for which the places will be computed, constraint: state != 0 && state->reachable_places != 0
	*	@return	2D array where a square has false value if place is unreachable, true if it is reachable
	*/
	array2d< bool >* map_places_reachable( game_state* state );

	/** @brief	Estimates dangerousness of places
	*	@param	state state for which the places will be computed, constraint: state != 0
	*	@return	2D array where one square contains maximum harm, that can enemy's units cause there by one shot
	*/
	array2d< unsigned int >* map_places_dangerous( game_state* state );

	/** @brief	Estimates dangerousness of places
	*	@param	state state for which the places will be computed, constraint: state != 0
	*	@return	2D array where one square contains total maximum, that can enemy's units cause there by one shot, but it doesnt consider other units as shield
	*/
	array2d< unsigned int >* map_places_dangerous_nobody( game_state* state );

	/** @brief	Estimates offensive coverage of places
	*	@param	state state for which the places will be computed, constraint: state != 0
	*	@return	2D array where one square contains total maximum, that can current player's units cause there by one shot
	*/
	array2d< unsigned int >* map_places_attackable( game_state* state );

	/** @brief	Estimates places to attack from
	*	@param	state state for which the places will be computed, constraint: state != 0
	*	@return	vector of 2D arrays where one square contains number of enemy's, that can be attacked from there, all per current player's unit
	*/
	vector< array2d< unsigned int >* >* map_places_range( game_state* state );

	/**	@brief	Computes score of state
	*	@param	state state for which the score will be computed, constraint: state != 0 && state->dangerous_places != 0 && state->range_places != 0
	*	@return	current player's score
	*/
	double game_state_estimate( game_state* state );


	/**	@brief	Tests if you can fire from [x,y] to [tx,ty] without considering other units
	*	@param	x starting x coordinate, constraint: 0 <= x < state->map->size_x
	*	@param	y starting y coordinate, constraint: 0 <= y < state->map->size_y
	*	@param	tx final x coordinate, constraint: 0 <= tx < state->map->size_x
	*	@param	ty final y coordinate, constraint: 0 <= ty < state->map->size_y
	*	@param	state state for which the track will be computed, constraint: state != 0 && state->map != 0
	*	@return	-1 if attack in not possible or distance if it is possible
	*/
	int ai_test_fire_nobody( unsigned int x, unsigned int y, unsigned int tx, unsigned int ty, game_state* state );

	/**	@brief	Tests if you can move from [x,y] to [tx,ty] without considering other units
	*	@param	sx starting x coordinate, constraint: 0 <= sx < state->map->size_x
	*	@param	sy starting y coordinate, constraint: 0 <= sy < state->map->size_y
	*	@param	dx final x coordinate, constraint: 0 <= tx < state->map->size_x
	*	@param	dy final y coordinate, constraint: 0 <= ty < state->map->size_y
	*	@param	state state for which the path will be computed, constraint: state != 0 && state->map != 0
	*	@return	-1 if move in not possible or distance if it is possible
	*/
	int ai_test_move_nobody( unsigned int sx, unsigned int sy, unsigned int dx, unsigned int dy, game_state* state );

	/**	@brief	Tests if you can fire from [x,y] to [tx,ty] with considering other units
	*	@param	x starting x coordinate, constraint: 0 <= x < state->map->size_x
	*	@param	y starting y coordinate, constraint: 0 <= y < state->map->size_y
	*	@param	tx final x coordinate, constraint: 0 <= tx < state->map->size_x
	*	@param	ty final y coordinate, constraint: 0 <= ty < state->map->size_y
	*	@param	state state for which the track will be computed, constraint: state != 0 && state->map != 0 && state->objects != 0
	*	@return	-1 if attack in not possible or distance if it is possible
	*/
	int ai_test_fire( unsigned int x, unsigned int y, unsigned int tx, unsigned int ty, game_state* state );

	/**	@brief	Tests if you can move from [x,y] to [tx,ty] with considering other units
	*	@param	sx starting x coordinate, constraint: 0 <= sx < state->map->size_x
	*	@param	sy starting y coordinate, constraint: 0 <= sy < state->map->size_y
	*	@param	dx final x coordinate, constraint: 0 <= tx < state->map->size_x
	*	@param	dy final y coordinate, constraint: 0 <= ty < state->map->size_y
	*	@param	state state for which the path will be computed, constraint: state != 0 && state->map != 0 && state->objects != 0
	*	@return	-1 if move in not possible or distance if it is possible
	*/
	int ai_test_move( unsigned int sx, unsigned int sy, unsigned int dx, int unsigned dy, game_state* state );

	/**	@brief	Tests if the player can buy the unit to [x,y]
	*	@param	pl buying player's id, constraint: pl >= 0
	*	@param	u unit to buy, constraint: u != 0
	*	@param	x x coordinate, where to place unit, constraint: 0 <= x < state->map->size_x
	*	@param	y y coordinate, where to place unit, constraint: 0 <= y < state->map->size_y
	*	@param	state state for which the path will be computed, constraint: state != 0 && state->map != 0 && state->objects != 0
	*	@return	-1 if move in not possible or price if it is possible
	*/
	int ai_test_buy( unsigned int pl, unit* u, unsigned int x, unsigned int y, game_state* state );


	/**	@brief	Simulates move command on given state
	*	@param	u unit to move, constraint: u != 0
	*	@param	x destination x coordinate, constraint: 0 <= x < state->map->size_x
	*	@param	y destination y coordinate, constraint: 0 <= y < state->map->size_y
	*	@param	state state where the movement will be done, constraint: state != 0 && state->map != 0 && state->objects != 0
	*	@return	true if suceeded, else false
	*/
	bool ai_move( unit* u, unsigned int x, unsigned int y, game_state* state );

	/**	@brief	Simulates fire command on given state
	*	@param	u attacking unit, constraint: u != 0
	*	@param	x target x coordinate, constraint: 0 <= x < state->map->size_x
	*	@param	y target y coordinate, constraint: 0 <= y < state->map->size_y
	*	@param	state state where the attack will be done, constraint: state != 0 && state->map != 0 && state->objects != 0
	*	@return	true if suceeded, else false
	*/
	bool ai_fire( unit* u, unsigned int x, unsigned int y, game_state* state );

	/**	@brief	Simulates buy command on given state
	*	@param	u unit to buy, constraint: u != 0
	*	@param	x x coordinate, where to place unit, constraint: 0 <= x < state->map->size_x
	*	@param	y y coordinate, where to place unit, constraint: 0 <= y < state->map->size_y
	*	@param	state state where the buy will be done, constraint: state != 0 && state->map != 0 && state->objects != 0
	*	@return	true if succeeded, else false
	*/
	bool ai_buy( unit* u, unsigned int x, unsigned int y, game_state* state );

	/**	@brief	Simulates sell command on given state
	*	@param	n unit's id
	*	@param	state state where the buy will be done, constraint: state != 0 && state->map != 0 && state->objects != 0
	*	@return	true if succeeded, else false
	*/
	bool ai_sell( unsigned int n, game_state* state );

	/**	@brief	Simulates next turn on given state
	*	@param	state state where the next turn will be done, constraint: state != 0 && state->current_player_id valid && state->players_count valid (for more info see constructor)
	*/
	void ai_next_turn( game_state* state );

	/**	@brief	Simulates buy end on given state
	*	@param	state state where the end buy will be done, constraint: state != 0 && state->current_player_id valid && state->players_count valid (for more info see constructor)
	*/
	void ai_end_buy( game_state* state );

	/**	@brief	Simulates given command on given state
	*	@param	state state where the command will be applied, constraint: stat != 0 && properities due to command (for more info see commands)
	*	@param cmd command to be applied
	*	@return	true if suceeded, else false
	*/
	bool apply_command( game_state* state, const string& cmd );

};



/**	@brief Game state with some aditional info
*	For AI purposes
*
*   \image html ai.jpeg
*/
class ai_game_state
{

public:

	/**	@brief Standart constructor
	*	useless
	*/
	ai_game_state()
	{
		state = 0;
	};

	/** @brief AI game state made from standart state
	*	for examle used in the beginnings
	*	@param gs standart game state, constraint: gs != 0
	*/
	ai_game_state( game_state* gs )
	{
		state = gs->clone();
		command = "";
	};

	/**	@brief AI game state made from standart state
	*	creates state from given state by applying cmd command on old state
	*	@param gs standart game state, constraint: gs != 0
	*	@param cmd command to be done
	*	@param apply_cmd whether apply commnad to given state, or only store it
	*/
	ai_game_state( game_state* gs, const string& cmd, bool apply_cmd = true )
	{
		state = gs->clone();
		if ( apply_cmd )
			ai_support_computations::apply_command( state, cmd );
		command = cmd;
	};

	/** @brief Creates deep clone of given state
	*	@return clone of the ai game state
	*/
	ai_game_state* clone()
	{
		ai_game_state* cl = new ai_game_state( this->state );
		cl->command = command;
		return cl;
	};

	//! Destructor deleted contained state
	~ai_game_state()
	{
		if ( state != 0 )
			delete state;
	};

	
	
	//! actual state
	game_state* state;

	//! command by which the state was affected
	string command;

};



/** @brief Base class for all AI algorithms
*
*   \image html ai.jpeg
*/
class algorithm: public QObject
{

	Q_OBJECT

public:

	//! Constructor
	algorithm() {};

	/** @brief Templated function for registering ai algorithm to the map
	*/
	template< class This >
	static void register_metpl()
	{
		ai_algorithms::get_instance()->register_algorithm( new This );
	};

	/**	@brief Function for obtaining name of the algorithm
	*	@return name of the algorithm
	*/
	string get_name()
	{
		return name;
	};

	/** @brief	Abstract cloning function
	*	@return	clone of the algorithm
	*/
	virtual algorithm* clone() = 0;

	/**	@brief Function for initialising of algorithm
	*	sets owner and connects algorithm to game core
	*	@param p this player will be set as owner, constraint: p != 0
	*	@param s optional param for some more init settings
	*	@return true if init successful, else false
	*/
	virtual bool init( player* p, const string& s = "" );

	/**	@brief	Function for obtaining the owner of the algorithm
	*	@return	owner of the algorithm
	*/
	player* get_owner();

	/** @brief Sets owner of the algorithm
	*	@param p this player will be set as owner, constraint: p != 0
	*/
	void set_owner( player* p );

	/** @brief Function called at the beginning of buy
	*	@param gs current game state
	*/
	virtual void get_buy_commands( const game_state* gs ) = 0;
	/** @brief Function called at the beginning of every turn
	*	@param gs current game state
	*/
	virtual void get_commands( const game_state* gs ) = 0;

	/** @brief Templated function for cloning of children
	*	@return pointer to the clone
	*/
	template< class This >
	This* clonetpl()
	{
		This* cl = new This();
		cl->set_owner( owner );
		return cl;
	};

	//! time consumed for decisions
	vector< double > time_cons;


protected:

	//! name of the algorithm
	string name;

	//! owner of the algorithm (player that uses the algorithm)
	player* owner;

	//! list of commands for current turn
	list< string > commands;


signals:

	//! @brief Qt singnal that sends one command
	void send_command( player*, const string& );



public slots:

	//! @brief Qt slot, when signal comes, next command will be send by send_command signal
	void next_command();

	//! @brief Qt slot for termination of the algorithm in case of game end
	void stop();

};



/** @brief Singleton that contains prototypes of AI algorithms
*
*   \image html ai.jpeg
*/
class ai_algorithms
{

public:

	/**	@brief	Function for obaining the singleton
	*	@return	pointer to the singleton
	*/
	static ai_algorithms* get_instance();

	//! Nothing to be done there
	void rebuild();

	//! list of algorithms
	map< string, algorithm* > algorithm_list;

	/**	@brief Registers AI algorithm to database
	*	@param newalg algorithm to register
	*/
	void register_algorithm( algorithm* newalg );


	/** @brief	Converts time from seconds to human readable format
	*	@param	seconds time in seconds
	*	@return	human readable time string
	*/
	static string sec_to_string( unsigned int seconds );

	/** @brief Suspends thread for scecific amount of miliseconds
	*	@param msecs number of miliseconds to suspend for
	*/
	static void suspend( unsigned int msecs );


	int x;



private:

	ai_algorithms( int _x );
	ai_algorithms( const ai_algorithms& );
	ai_algorithms& operator=( const ai_algorithms& x );
	//! @brief Deletes algorithms map
	~ai_algorithms();

};



#endif